package com.nado.extensions.util;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Enumeration;

/**
 * 리소스를 읽어 들여 다양한 형태로 제공하는 Loader 클래스.
 * <p/>
 * 리소스를 읽어들이는 클래스 Loader는 다음 순서대로 resolving된다.
 * <ul>
 * <li>현재 스레드의 Context 클래스 Loader</li>
 * <li>ResourceLoader 클래스를 처음 읽어 들였던 클래스 Loader</li>
 * </ul>
 * <p/>
 * <p/>
 * 클래스 패스에 존재하는 자원은 다음과 같은 형태로 제공된다.
 * <ul>
 * <li>{@link URL}</li>
 * <li>{@link File}</li>
 * <li>Filename</li>
 * <li>{@link InputStream}</li>
 * </ul>
 */
public class ResourceLoader {
	private final Log log = LogFactory.getLog(ResourceLoader.class);

	/**
	 * internal Class Loader
	 */
	private ClassLoader classLoader;

	/**
	 * 우선적으로 콘텍스트 클래스 로더를 찾으며 이를 지정하지 않은 경우
	 * {@link ResourceLoader}의 defining class loader를 사용한다.
	 */
	public ResourceLoader() {
		classLoader = Thread.currentThread().getContextClassLoader();
		if (classLoader == null) {
			classLoader = this.getClass().getClassLoader();
		}
		if (classLoader == null) {
			classLoader = ClassLoader.getSystemClassLoader();
		}
	}

	/**
	 * 사용자가 전달한 클래스 로더를 사용한다.
	 *
	 * @param classLoader 사용자 지정 {@link ClassLoader}
	 */
	public ResourceLoader(ClassLoader classLoader) {
		this.classLoader = classLoader;
	}

	/**
	 * 현재 {@link ResourceLoader}가 사용하는 클래스 로더를 반환한다.
	 *
	 * @return {@link ClassLoader}
	 */
	public ClassLoader getClassLoader() {
		return classLoader;
	}

	/**
	 * 클래스 패스에 있는 자원을 URL Type으로 반환한다.
	 *
	 * @param resourceName 찾고자 하는 자원명
	 */
	public URL getResource(String resourceName) {
		return classLoader.getResource(resourceName);
	}

	/**
	 * 같은 이름에 해당하는 {@link URL} {@link Enumeration}을 반환한다.
	 * 이름에 해당하는 리소스가 없는 경우에도 비어있는 {@link Enumeration}을 반환한다.
	 */
	public Enumeration<URL> getUrls(String resourceName) {
		Enumeration<URL> urls = null;
		try {
			urls = classLoader.getResources(resourceName);
		} catch (IOException e) {
			log.warn(resourceName + " does not exist");
		}
		return urls;
	}

	/**
	 * 클래스 패스에 있는 자원을 InputStream Type으로 반환한다.
	 *
	 * @param resourceName 찾고자 하는 자원명
	 */
	public InputStream getResourceAsStream(String resourceName) {
		return classLoader.getResourceAsStream(resourceName);
	}

	/**
	 * 클래스 패스에 있는 자원을 File Type으로 반환한다.
	 *
	 * @param resourceName 찾고자 하는 자원명
	 */
	public File getFile(String resourceName) {
		String file = getFilename(resourceName);
		if (file == null) {
			return null;
		}

		return new File(file);
	}

	/**
	 * 클래스 패스에 있는 자원의 filename을 반환한다.
	 *
	 * @param resourceName 찾고자 하는 자원명
	 */
	public String getFilename(String resourceName) {
		URL url = getResource(resourceName);
		if (url == null) {
			log.warn(resourceName + " does not exist");
			return null;
		}

		return url.getFile();
	}

	/**
	 * 클래스 패스 내에 자원이 존재하는지 여부를 반환한다.
	 *
	 * @param resourceName 찾고자 하는 자원명
	 */
	public boolean fileExists(String resourceName) {
		return getResource(resourceName) != null;
	}
}
